#pragma once
#include <iostream>
#include <functional>

#include <cerrno>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <wait.h>
#include <pthread.h>

#include "Log.hpp"
#include "sockaddr.hpp"
#include "Comm.hpp"
#include "nocopy.hpp"
#include "ProtectResourse.hpp"
#include "threadpool.hpp"

#define DEFAULT_LISTEN_SOCKFD -1
#define DEFAULT_BACKLOG_SIZE 5
#define BUFFERSIZE 1024

using task_t = std::function<void()>;

class Server : public nocopy //防止拷贝
{
private:
    // static void* Thread_Func(void* arg) //多线程
    // {
    //     pthread_detach(pthread_self()); //不需要主线程进行回收了
    //     Receive((long)arg);
    //     return nullptr;
    // }

    
    /*static */ void Receive(int sockfd)  //static也是为了多线程加的
    {
        //这里是获取连接成功， 开始接收信息
        char buffer[BUFFERSIZE];
        while(true)
        {
            //接收数据
            ssize_t ret_read = read(sockfd, buffer, sizeof(buffer) - 1);
            //读错误
            if(ret_read < 0)
                CheckRet(ret_read, server_name, func_types[4]);
            //读到了结尾，既client端关闭
            else if(ret_read == 0)
            {
                std::cout << "client exit" << std::endl;
                break;
            }
            //读到数据
            else
            {
                buffer[ret_read] = 0;
                std::cout << "Client# " << buffer << std::endl;
                std::string rebuffer;
                rebuffer += "[sockfd=";
                rebuffer += std::to_string(sockfd);
                rebuffer += "]";
                rebuffer += "[Server]# ";
                rebuffer += buffer;
                ssize_t ret_write = write(sockfd, rebuffer.c_str(), rebuffer.size());
                CheckRet(ret_write, server_name, func_types[5]);
            }
        }
        
        close(sockfd);
    }
public:
    Server(uint16_t port)
        : _listen_sockfd(DEFAULT_LISTEN_SOCKFD)
        , _sk(port)
    {}
    ~Server()
    {}
    //初始化Server端
    void Init()
    {
        //获取listen套接字，此套接字用于监听client的请求连接
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        //检查返回值
        CheckRet(_listen_sockfd, server_name, func_types[0]);

        //解决server主动关闭，无法重启，bind失败的问题
        int opt = 1;
        setsockopt(_listen_sockfd, 
                   SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 
                   &opt, sizeof(opt));

        //bind
        int ret_bind = ::bind(_listen_sockfd, (const sockaddr*)(&_sk.addr_in()), _sk.len());
        //检查返回值
        CheckRet(ret_bind, server_name, func_types[1]);

        //这里表示监听client,监听到一个client，将其入队列，再由accept一个一个拿
        int ret_listen = listen(_listen_sockfd, DEFAULT_BACKLOG_SIZE);
        //检查返回值
        CheckRet(ret_listen, server_name, func_types[2]);

    }
    
    //server开始服务
    void Start()
    {
        //【v4：线程池】
        ThreadPool<task_t>::GetOnce()->Detach_Threads(); //分离线程池的线程
        ThreadPool<task_t>::GetOnce()->Start_Task(); //让线程池的线程执行任务

        //使用循环的原因：server可能会获取多个连接，同时有多个client要通信
        //所以我们就要挨个建立连接（但这里只是单进程，只能获取一个client的连接）
        while(true)
        {
            //输出型参数，获取client的sockaddr配置信息
            sockaddr_in peer; 
            socklen_t len = sizeof(peer);
            //建立连接！
            int sockfd = accept(_listen_sockfd, (sockaddr*)(&peer), &len); //这里不需要进行错误排查，没必要，没有收到连接就阻塞
            //获取连接成功
            CheckRet(0, server_name, func_types[3]);

            // //【v1:多进程】//这里需要父进程等待子进程退出，\
            // 但是子进程执行的是一个while1，一直不退出
            // 那父进程就会while1地去等待，就也陷入死循环了，无法去继续获取连接

            // 解决方法1: 创建孙子进程
            // int id = fork();
            // if(id < 0)
            // {
            //     close(sockfd);
            //     continue;
            // }
            // else if(id == 0) //子进程进行IO操作
            // {
            //     close(_listen_sockfd); 
            //     //让孙子进程接收信息并发送回去
            //     if(fork() == 0)  
            //     {
            //         Receive(sockfd);
            //         close(sockfd);
            //         exit(0);
            //     }
            //     //子进程退出，供父进程等待成功
            //     exit(0);
            // }
            // else
            // {
            //     waitpid(id, nullptr, 0); //父进程关IO的sockfd，这里父子进程都要关闭自己不需要的fd，防止文件描述符表溢出
            //     close(sockfd); //父进程继续进行获取连接
            // }

            //解决方法2: 捕获SIGCHLD信号
            // int id = fork();
            // if(id == 0) 
            // {
            //     close(_listen_sockfd); 
            //     Receive(sockfd);
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);

            //【v2:进程池】\
            //...

            // //【v3：多线程】\
            //
            // pthread_t new_pthread;
            // pthread_create(&new_pthread, nullptr, Thread_Func, reinterpret_cast<void*>(sockfd));

            //【v4：线程池版本】\
            //

            ThreadPool<task_t>::GetOnce()->Push(std::bind(&Server::Receive, this, sockfd)); //向线程池派发任务
        }
    }

private:
    int _listen_sockfd;
    Sockaddr _sk;
};